﻿using Ardalis.GuardClauses;
using CleanArchitecture.Core;
using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Helper;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Core.Interfaces.Cache;
using CleanArchitecture.Infrastructure.ActiveLogExtension;
using CleanArchitecture.Infrastructure.Entities;
using CleanArchitecture.Infrastructure.Entities.Business;
using CleanArchitecture.Infrastructure.Entities.Common;
using CleanArchitecture.Infrastructure.Entities.Settings;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.Business;
using CleanArchitecture.Infrastructure.Interfaces.Common;
using CleanArchitecture.Infrastructure.Interfaces.Media;
using CleanArchitecture.Infrastructure.Models.Business.Blog;
using CleanArchitecture.Infrastructure.Models.Business.News;
using CleanArchitecture.Infrastructure.Services;
using CleanArchitecture.Web.Backend.BackendApi;
using CleanArchitecture.Web.Controllers.Frontend.FrontendApi;
using CleanArchitecture.Web.Factories;
using Masuit.LuceneEFCore.SearchEngine;
using Masuit.LuceneEFCore.SearchEngine.Interfaces;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ClientApp.Web.Api
{
    public class ColumnArticleRequest
    {
        public int[] ColumnIds { get; set; }
        public int TopN { get; set; } = 1;
        public bool KeepContents { get; set; } = false;
    }
    public class ApiArticle : BaseApiController
    {
        private readonly ILocker _locker;
        private readonly IWebHelper _webHelper;
        private readonly IBlogService _blogService;
        private readonly ISettingService _settingService;
        private readonly IDateTimeHelper _datetimeHelper;
        private readonly IStaticCacheManager _cacheManager;
        private readonly ILanguageService _languageservice;
        private readonly IActiveLogService _activelogService;
        private readonly IGenericAttributeService _gaService;
        private readonly IFileUploadService _fileuploadService;
        private readonly IBusinessModelFactory _businessmodelFactory;
        private readonly ISearchEngine<AppDbObjectContext> _searchEngine;
        public ApiArticle(IBusinessModelFactory businessmodelFact,
            ILocker locker,
            IWebHelper webHelper,
            IBlogService blogService,
            ISettingService settingService,
            IDateTimeHelper datetimeHelper,
            ILanguageService languageService,
            IStaticCacheManager cacheManager,
            IGenericAttributeService gaService,
            IActiveLogService activelogService,
            IFileUploadService fileuploadService,
            ISearchEngine<AppDbObjectContext> searchEngine)
        {
            this._locker = locker;
            this._webHelper = webHelper;
            this._gaService = gaService;
            this._blogService = blogService;
            this._cacheManager = cacheManager;
            this._searchEngine = searchEngine;
            this._datetimeHelper = datetimeHelper;
            this._settingService = settingService;
            this._languageservice = languageService;
            this._activelogService = activelogService;
            this._fileuploadService = fileuploadService;
            this._businessmodelFactory = businessmodelFact;
        }
        #region  Column 
        /// <summary>
        /// 获取前端页面展示栏目树
        /// </summary>
        /// <returns></returns>
        [HttpGet(FrontendApiRouteConfig.FrontendGetColumnTree)]
        public IActionResult FrontendGetColumnTree(int langid = 0)
        {
            try
            {
                var languages = _languageservice.GetLanguagesAll();
                Language lang = null;
                if (langid <= 0)//Get Default Language as Chinese
                    lang = languages.Single(l => l.LanguageCode == "zh");
                else lang = languages.FirstOrDefault(l => l.Id == langid);
                if (lang == null) throw new NowaException(ApiResponseMessages.EntityNotFound);
                var columntree = _businessmodelFactory.PrepareColumnItemModelsForFront(lang.Id);
                return SuccessJson("", columntree);
            }
            catch (Exception ex)
            {
                return ErrorJson(ApiResponseMessages.ApiResponseError);
            }
        }
        #endregion

        #region Components
        //公用组件API
        [HttpGet(FrontendApiRouteConfig.FrontendGetAdvertiseData)]
        public IActionResult FrontendGetAdvertiseData(string hashid)
        {
            try
            {
                if (string.IsNullOrEmpty(hashid))
                    throw new ArgumentNullException(ApiResponseMessages.EntityNotFound);
                int advItemId = hashid.IdDecode();
                if (advItemId <= 0)
                    throw new NowaException(ApiResponseMessages.EntityNotFound);
                var advItemModel = _businessmodelFactory.PrepareAdvertiseBannerModel(advItemId);
                return SuccessJson("", advItemModel);
            }
            catch (Exception ex)
            {
                return ErrorJson(ApiResponseMessages.ApiResponseError);
            }
        }
        [HttpGet(FrontendApiRouteConfig.FrontendGetAdvertiseData4Mobile)]
        public IActionResult FrontendGetAdvertiseData4Mobile(int aid)
        {
            return FrontendGetAdvertiseData(aid.IdEncode());
        }
        [HttpPost(FrontendApiRouteConfig.FrontendLoadClientSettings)]
        public async Task<IActionResult> FrontendLoadClientSettings([FromBody]int datatype = 100)
        {
            try
            {
                Guard.Against.NegativeOrZero(datatype, "ClientSettingType");
                EntityEnumConfig.DataType result = typeof(EntityEnumConfig.DataType).IsEnumDefined(datatype) && datatype >= 100 //过滤非Client(站点)客户端类型
                                             ? (EntityEnumConfig.DataType)datatype
                                             : EntityEnumConfig.DataType.ClientPC; //Default
                ClientSiteSettings clientSettingsModel;
                switch (result)
                {
                    case EntityEnumConfig.DataType.ClientMobile:
                        clientSettingsModel = _settingService.LoadSetting<ClientSiteMobileSettings>();
                        break;
                    case EntityEnumConfig.DataType.ClientPC:
                    default:
                        clientSettingsModel = _settingService.LoadSetting<ClientSitePCSettings>();
                        break;
                }
                return SuccessJson("", clientSettingsModel);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        #endregion

        #region News
        /// <summary>
        /// 按栏目获取文章列表，PC端 Razor页面调用
        /// </summary>
        /// <param name="columnhash"></param>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <param name="isstatic">增加浏览量统计功能，默认不统计</param>
        /// <returns></returns>
        [HttpGet(FrontendApiRouteConfig.FrontendLoadNewsList)]
        public async Task<IActionResult> FrontendLoadNewsList(string columnhash, int page = 1, int pagesize = 20, bool isstatic = false)
        {
            try
            {
                if (string.IsNullOrEmpty(columnhash))
                    throw new ArgumentNullException(nameof(columnhash));
                int cid = columnhash.IdDecode();
                if (cid <= 0)
                    throw new NowaException(ApiResponseMessages.EntityNotFound);
                var curColumn = _businessmodelFactory.PrepareColumnItemModel(cid);
                if (curColumn == null)
                    throw new NowaException(ApiResponseMessages.EntityNotFound);
                bool keepContents = curColumn.LayoutListTemplate.Equals("JobList", StringComparison.InvariantCultureIgnoreCase);
                var searchModel = new NewsItemSearchModel
                {
                    ColumnId = cid,
                    RoleIds = null,
                    Page = page,
                    PageSize = pagesize
                };
                var newslist = _businessmodelFactory.PrepareNewsItemListModel(searchModel, keepContents);
                if (newslist != null && isstatic)
                {
                    int[] newsids = newslist.Data?.Select(news => news.Id).ToArray() ?? null;
                    var staticResult = _activelogService.StatisticsNewsCountsByNewsIds(newsids);
                    newslist.CustomProperties.Add("NewsStatistics", staticResult);
                }
                return SuccessJson("", newslist);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        /// <summary>
        /// 按栏目获取文章列表，手机端调用Api
        /// </summary>
        /// <param name="cid"></param>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <param name="isstatic">增加浏览量统计功能，默认不统计</param>
        /// <returns></returns>
        [HttpGet(FrontendApiRouteConfig.FrontendLoadNewsList4Mobile)]
        public async Task<IActionResult> FrontendLoadNewsList4Mobile(int cid, int page = 1, int pagesize = 20, bool isstatic = false)
        {
            return await FrontendLoadNewsList(cid.IdEncode(), page, pagesize, isstatic);
        }
        /// <summary>
        /// 获取指定文章内容 手机端(前后端分离)调用接口
        /// </summary>
        /// <param name="nid">指定文章的Id</param>
        /// <param name="cid">
        ///     默认为0，前端不用传参, 仅当以下情况时需要提供栏目菜单Id
        ///     1：文章 nid 为0： 需要提供 菜单栏目Id，此时栏目菜单导航可能为单页详情页面,如 "关于我们"
        ///     ...如有其他情况，以后在此说明
        /// </param>
        /// <returns></returns>
        [HttpGet(FrontendApiRouteConfig.FrontendLoadNewsDetail)]
        public async Task<IActionResult> FrontendLoadNewsDetail(int nid = 0, int cid = 0)
        {
            try
            {
                NewsItemModel detail = null;
                if (nid == 0 && cid == 0) throw new NowaException("Exception404");
                else if (nid == 0 && cid > 0)
                {
                    var menuItem = _businessmodelFactory.PrepareColumnItemModel(cid);
                    if (menuItem == null) throw new NowaException("Exception404");
                    if (menuItem.LayoutType != EntityEnumConfig.PageLayoutType.SingleDetailPage)
                        throw new NowaException("Redirect-{0}", menuItem.UrlRoute);
                    else if (/*menuItem.IsDisplay == EntityEnumConfig.YesorNo.No || */
                            menuItem.IsActive == EntityEnumConfig.YesorNo.No ||
                            menuItem.ColumnType != EntityEnumConfig.ColumnType.NewsColumn)
                        throw new NowaException("Exception404");
                    else
                        detail = _businessmodelFactory.PrepareColumnNewsItemModelForFront(menuItem.Id);
                }
                else if (nid > 0)
                    detail = _businessmodelFactory.PrepareNewsItemModelForFront(nid);
                else //bid < 0 || cid < 0 or other situation
                    throw new NowaException("Exception404");

                if (detail == null) throw new NowaException("Exception404");

                //获取 统计信息
                //get expiration time
                var expiration = TimeSpan.FromSeconds(120);
                string CacheKey = $"Visit_NewsItemId_{detail.Id.IdEncode()}";
                //execute task with lock
                bool isLogged = _locker.PerformActionWithLock(CacheKey, expiration, () =>
                {
                    var time = DateTime.UtcNow;
                    var logitem = new ActiveLog
                    {
                        ActiveName = ActiveLogName.VisitNewsPage,
                        EntityId = detail.Id,
                        EntityName = nameof(NewsItem),
                        CustomerId = 0,
                        Detail = "用户浏览新闻/文章",
                        IpAddress = _webHelper.GetCurrentIpAddress(),
                        CreatedOnTimeUtc = time,
                        Month = time.Month,
                        Year = time.Year
                    };
                    _activelogService.InsertLog(logitem);
                });
                if (isLogged)
                {
                    var NewsVisitCount = _cacheManager.Get(CacheKey, () =>
                    {
                        int[] articleids = { detail.Id };
                        var staticResult = _activelogService.StatisticsNewsCountsByNewsIds(articleids);
                        if (staticResult != null)
                        {
                            if (staticResult.Count > 0)
                                return staticResult;
                        }
                        return new List<PageVisitQuery> { new PageVisitQuery() { Id = detail.Id, VisitCount = 1, LikeCount = 1 } };
                    }, expiration.Minutes);

                    detail.CustomProperties.Add("ArticleStatistics", NewsVisitCount);
                }

                return SuccessJson("", detail);
            }
            catch (Exception ex)
            {
                if (ex is NowaException && ex.Message == "Exception404")
                    return PageNotFoundJson(ApiResponseMessages.EntityNotFound);
                else if (ex is NowaException && ex.Message.StartsWith("Redirect"))
                {
                    int start = ex.Message.IndexOf('-');
                    return RedirectJson("Url Redirect", ex.Message.Substring(start, ex.Message.Length - start - 1));
                }
                else return ErrorJson(ex.Message);
            }
        }
        /// <summary>
        /// 前端展示页面根据栏目菜单Id获取每个栏目下前N条文章信息
        /// 与FrontendLoadNewsDetailsByColumnIds区别在于：
        /// FrontendLoadNewsDetailsByColumnIds 只获取发布日期最新的前N条记录
        /// FrontendLoadNewsDetailsForEachColumn 首先获取每个栏目下推荐置顶的前N条记录
        /// 当推荐置顶记录不够前N条时，获取最新发布的记录补足
        /// </summary>
        /// <param name="columnids"></param>
        /// <param name="topN"></param>
        /// <param name="keepContents"></param>
        /// <returns></returns>
        [HttpPost(FrontendApiRouteConfig.FrontendLoadNewsDetailsForEachColumn)]
        public async Task<IActionResult> FrontendLoadNewsDetailsForEachColumn([FromBody]ColumnArticleRequest caReq)
        {
            try
            {
                if (caReq == null)
                    throw new ArgumentNullException(nameof(ColumnArticleRequest));
                var newsItemsDict = _businessmodelFactory.PrepareNewsItemByEachColumnIdForFront(caReq.ColumnIds, caReq.TopN, caReq.KeepContents);
                return SuccessJson("", newsItemsDict);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        /// <summary>
        /// 按栏目列表分别获取每个栏目前N条记录
        /// </summary>
        /// <param name="columnids"></param>
        /// <param name="topN"></param>
        /// <param name="keepContents"></param>
        /// <returns></returns>
        [HttpPost(FrontendApiRouteConfig.FrontendLoadNewsDetailsByColumnIds)]
        public async Task<IActionResult> FrontendLoadNewsDetailsByColumnIds([FromBody]ColumnArticleRequest caReq)
        {
            try
            {
                if (caReq == null)
                    throw new ArgumentNullException(nameof(ColumnArticleRequest));
                var newsItems = _businessmodelFactory.PrepareNewsItemByColumnIdsForFront(caReq.ColumnIds, caReq.TopN, caReq.KeepContents);
                return SuccessJson("", newsItems);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        #region SearchService
        [HttpGet(FrontendApiRouteConfig.FrontendSearchService)]
        public async Task<IActionResult> SearchService(string s, int page = 1, int pagesize = 20)
        {
            try
            {
                if (string.IsNullOrEmpty(s))
                    throw new NowaException(ApiResponseMessages.ArgumentCannotBeNull);
                var searchResult = _searchEngine.ScoredSearch<NewsItem>(new SearchOptions(s, page, pagesize, typeof(NewsItem)));
                if (searchResult != null)
                {
                    searchResult.Results.ToList().ForEach((item) => {
                        //借用字段存储 HashId 便于前端生成Url
                        item.Entity.MetaTitle = item.Entity.Id.IdEncode();
                        item.Entity.MetaDescription = item.Entity.FKColumnId.IdEncode();
                        item.Entity.CreatedOnTimeUtc = _datetimeHelper.ConvertToUserTime(item.Entity.CreatedOnTimeUtc, DateTimeKind.Local);
                    });
                }
                return SuccessJson("", searchResult);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        #endregion
        #endregion

        #region Blog
        [HttpGet(FrontendApiRouteConfig.FrontendLoadBlogList)]
        public async Task<IActionResult> FrontendLoadBlogList(int cid = 0, int page = 1, int pagesize = 20)
        {
            try
            {
                var searchModel = new BlogItemSearchModel
                {
                    ColumnId = cid,
                    Page = page,
                    PageSize = pagesize
                };
                var bloglist = _businessmodelFactory.PrepareBlogItemListModel(searchModel);
                if (bloglist != null)
                {
                    int[] blogids = bloglist.Data?.Select(blog => blog.Id).ToArray() ?? null;
                    var staticResult = _activelogService.StatisticsBlogCountsByBlogIds(blogids);
                    bloglist.CustomProperties.Add("BlogStatistics", staticResult);
                }
                return SuccessJson("", bloglist);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        /// <summary>
        /// 根据浏览量获取热门文章
        /// </summary>
        /// <param name="langid"></param>
        /// <returns></returns>
        [HttpGet(FrontendApiRouteConfig.FrontendLoadHotBlogList)]
        public async Task<IActionResult> FrontendLoadHotBlogList(int langid = 0, int top = 10)
        {
            try
            {
                var supportLangs = _languageservice.GetLanguagesAll();
                if (!supportLangs.Any(l => l.Id == langid))
                    langid = supportLangs.FirstOrDefault(l => l.LanguageCode == "zh")?.Id ?? 0;
                if (top > 50) top = 10; //条件限制，不允许大数据量查询
                var hotbloglist = _blogService.GetHotBlogItems(langid, top);
                return SuccessJson("", hotbloglist);
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        /// <summary>
        /// 获取指定博客文章内容
        /// </summary>
        /// <param name="bid">指定博客文章的Id</param>
        /// <param name="cid">
        ///     默认为0，前端不用传参, 仅当以下情况时需要提供栏目菜单Id
        ///     1：博客文章 bid 为0： 需要提供 菜单栏目Id，此时栏目菜单导航可能为单页详情页面,如 "关于我们"
        ///     ...如有其他情况，以后在此说明
        /// </param>
        /// <returns></returns>
        [HttpGet(FrontendApiRouteConfig.FrontendLoadBlogDetail)]
        public async Task<IActionResult> FrontendLoadBlogDetail(int bid = 0, int cid = 0)
        {
            try
            {
                BlogItemModel detail = null;
                if (bid == 0 && cid == 0) throw new NowaException("Exception404");
                else if (bid == 0 && cid > 0)
                {
                    var menuItem = _businessmodelFactory.PrepareColumnItemModel(cid);
                    if (menuItem == null) throw new NowaException("Exception404");
                    if (menuItem.LayoutType != EntityEnumConfig.PageLayoutType.SingleDetailPage)
                        throw new NowaException("Redirect-{0}", menuItem.UrlRoute);
                    else if (menuItem.IsDisplay == EntityEnumConfig.YesorNo.No
                         || menuItem.IsActive == EntityEnumConfig.YesorNo.No
                         || menuItem.ColumnType != EntityEnumConfig.ColumnType.BlogColumn)
                        throw new NowaException("Exception404");
                    else //Search and Log BlogVisit Activity
                        detail = _businessmodelFactory.GetBlogItemModelForSinglePageColumn(menuItem.Id, true);
                }
                else if (bid > 0) //Search and Log BlogVisit Activity
                    detail = _businessmodelFactory.PrepareBlogItemModel(bid, true);
                else //bid < 0 || cid < 0 or other situation
                    throw new NowaException("Exception404");

                if (detail == null) throw new NowaException("Exception404");

                //图片相册,视频列表，文件列表，音频列表查询
                IList<GenericAttribute> blogItemExtraInfo = _gaService.GetAttributesForEntity(detail.Id, nameof(BlogItem));
                if (blogItemExtraInfo != null)
                {
                    IList<FileUpload> fileList = null;
                    int[] fileIds = null;
                    foreach (var attribute in blogItemExtraInfo)
                    {
                        CommonHelper.SetProperty(detail, attribute.Key, attribute.Value);
                        if (attribute.Key.IndexOf("Ids") > 0)
                        {
                            fileIds = attribute.Value.Split(',').Select(Int32.Parse).ToArray();
                            fileList = _fileuploadService.GetFileUploadList(fileIds);
                            string propName = attribute.Key.Substring(0, attribute.Key.IndexOf("Ids")) + "Urls";
                            CommonHelper.SetProperty(detail, propName, fileList.Select(f => _fileuploadService.GetFileUrl(f)).ToList());
                        }
                    }
                }
                //获取 统计信息
                int[] blogids = { detail.Id };
                var staticResult = _activelogService.StatisticsBlogCountsByBlogIds(blogids);
                detail.CustomProperties.Add("BlogStatistics", staticResult);
                return SuccessJson("", detail);
            }
            catch (Exception ex)
            {
                if (ex is NowaException && ex.Message == "Exception404")
                    return PageNotFoundJson(ApiResponseMessages.EntityNotFound);
                else if (ex is NowaException && ex.Message.StartsWith("Redirect"))
                {
                    int start = ex.Message.IndexOf('-');
                    return RedirectJson("Url Redirect", ex.Message.Substring(start, ex.Message.Length - start - 1));
                }
                else return ErrorJson(ex.Message);
            }
        }
        /// <summary>
        /// 记录点赞博客日志
        /// </summary>
        /// <param name="bid"></param>
        /// <returns></returns>
        [HttpPost(FrontendApiRouteConfig.FrontendLogLikeBlogEvent)]
        public async Task<IActionResult> FrontendLogLikeBlogEvent([FromBody]int bid)
        {
            try
            {
                Guard.Against.NegativeOrZero(bid, "Blogid");
                IBlogService blogService = EngineContext.Current.Resolve<IBlogService>();
                BlogItem blog = blogService.GetBlogItemById(bid);
                if (blog == null) throw new NowaException(ApiResponseMessages.EntityNotFound);
                else if (blog.IsActive == EntityEnumConfig.YesorNo.No)
                    throw new NowaException(ApiResponseMessages.EntityNotFound);
                //Log Like blog Event
                blog.LogLikeBlogEvent();
                return SuccessJson("");
            }
            catch (Exception ex)
            {
                return ErrorJson(ex.Message);
            }
        }
        #endregion
    }
}
